{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PDF RAG 에 대한 RAGAS 평가"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# API 키를 환경변수로 관리하기 위한 설정 파일\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# API 키 정보 로드\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# LangSmith 추적을 설정합니다. https://smith.langchain.com\n",
    "# !pip install -qU langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름을 입력합니다.\n",
    "logging.langsmith(\"Project-RAG-With-Evaluation\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 실습에 활용한 문서\n",
    "\n",
    "소프트웨어정책연구소(SPRi) - 2023년 12월호\n",
    "\n",
    "- 저자: 유재흥(AI정책연구실 책임연구원), 이지수(AI정책연구실 위촉연구원)\n",
    "- 링크: https://spri.kr/posts/view/23669\n",
    "- 파일명: `SPRI_AI_Brief_2023년12월호_F.pdf`\n",
    "\n",
    "_실습을 위해 다운로드 받은 파일을 `data` 폴더로 복사해 주시기 바랍니다_\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## PDF RAG 체인"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from rag.pdf import PDFRetrievalChain\n",
    "\n",
    "# PDF 문서를 로드\n",
    "pdf = PDFRetrievalChain([\"data/SPRI_AI_Brief_2023년12월호_F.pdf\"]).create_chain()\n",
    "\n",
    "# retriever 와 chain을 생성\n",
    "pdf_retriever = pdf.retriever\n",
    "pdf_chain = pdf.chain"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## RAGAS 평가\n",
    "\n",
    "ragas 평가를 위한 클래스를 정의합니다.\n",
    "\n",
    "평가 metric 은 `answer_relevancy` 와 `faithfulness` 를 사용합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from datasets import Dataset\n",
    "from langchain_core.documents import Document\n",
    "from ragas.metrics import answer_relevancy, faithfulness\n",
    "from ragas import evaluate\n",
    "from typing import List, Dict\n",
    "\n",
    "\n",
    "class RagEvaluator:\n",
    "    def __init__(self):\n",
    "        # 데이터 저장을 위한 리스트 초기화\n",
    "        self.questions: List[str] = []\n",
    "        self.answers: List[str] = []\n",
    "        self.contexts: List[List[Document]] = []\n",
    "\n",
    "    def add_sample(self, question: str, answer: str, context: List[Document]):\n",
    "        \"\"\"평가할 데이터 샘플을 추가합니다.\"\"\"\n",
    "        self.questions.append(question)\n",
    "        self.answers.append(answer)\n",
    "        context_list = [doc.page_content for doc in context]\n",
    "        self.contexts.append(context_list)\n",
    "\n",
    "    def get_samples(self) -> Dict:\n",
    "        \"\"\"현재까지 저장된 모든 샘플을 딕셔너리 형태로 반환합니다.\"\"\"\n",
    "        return {\n",
    "            \"question\": self.questions,\n",
    "            \"answer\": self.answers,\n",
    "            \"contexts\": self.contexts,\n",
    "        }\n",
    "\n",
    "    def evaluate_all(self):\n",
    "        \"\"\"저장된 데이터에 대해 RAG 평가를 수행합니다.\"\"\"\n",
    "        if not self.questions:\n",
    "            raise ValueError(\n",
    "                \"평가할 데이터가 없습니다. add_sample()을 통해 데이터를 먼저 추가해주세요.\"\n",
    "            )\n",
    "\n",
    "        # Dataset 생성\n",
    "        dataset = Dataset.from_dict(self.get_samples())\n",
    "\n",
    "        # 평가 수행\n",
    "        score = evaluate(dataset, metrics=[answer_relevancy, faithfulness])\n",
    "\n",
    "        return score.to_pandas()\n",
    "\n",
    "    def evaluate_last(self):\n",
    "        \"\"\"마지막 샘플에 대해 RAG 평가를 수행합니다.\"\"\"\n",
    "        if not self.questions:\n",
    "            raise ValueError(\n",
    "                \"평가할 데이터가 없습니다. add_sample()을 통해 데이터를 먼저 추가해주세요.\"\n",
    "            )\n",
    "\n",
    "        last_sample = {\n",
    "            \"question\": [self.get_samples()[\"question\"][-1]],\n",
    "            \"answer\": [self.get_samples()[\"answer\"][-1]],\n",
    "            \"contexts\": [self.get_samples()[\"contexts\"][-1]],\n",
    "        }\n",
    "\n",
    "        dataset = Dataset.from_dict(last_sample)\n",
    "        score = evaluate(dataset, metrics=[answer_relevancy, faithfulness])\n",
    "        return score.to_pandas()\n",
    "\n",
    "    def clear(self):\n",
    "        \"\"\"평가 데이터를 초기화합니다.\"\"\"\n",
    "        self.questions = []\n",
    "        self.answers = []\n",
    "        self.contexts = []\n",
    "\n",
    "\n",
    "# 사용 예시\n",
    "evaluator = RagEvaluator()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 실행"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_teddynote.messages import stream_response\n",
    "\n",
    "\n",
    "def ask(question: str):\n",
    "    context = pdf_retriever.invoke(question)\n",
    "    response = pdf_chain.stream(\n",
    "        {\n",
    "            \"question\": question,\n",
    "            \"context\": context,\n",
    "        }\n",
    "    )\n",
    "    output = stream_response(response, return_output=True)\n",
    "    evaluator.add_sample(question, output, context)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "삼성전자가 만든 생성형 AI 모델은 '삼성 가우스'입니다.\n",
      "\n",
      "**Source**\n",
      "- data/SPRI_AI_Brief_2023년12월호_F.pdf (page 12)"
     ]
    }
   ],
   "source": [
    "# 질문 실행\n",
    "ask(\"삼성전자가 만든 생성형 AI 모델은 무엇인가요?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "구글이 앤스로픽에 투자한 금액은 최대 20억 달러입니다. 이 중 5억 달러를 우선 투자하고, 향후 15억 달러를 추가로 투자할 계획입니다.\n",
      "\n",
      "**Source**\n",
      "- data/SPRI_AI_Brief_2023년12월호_F.pdf (page 13)"
     ]
    }
   ],
   "source": [
    "# 질문 실행\n",
    "ask(\"구글이 anthropic 에 투자한 금액은 얼마인가요?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'question': ['삼성전자가 만든 생성형 AI 모델은 무엇인가요?', '구글이 anthropic 에 투자한 금액은 얼마인가요?'],\n",
       " 'answer': [\"삼성전자가 만든 생성형 AI 모델은 '삼성 가우스'입니다.\\n\\n**Source**\\n- data/SPRI_AI_Brief_2023년12월호_F.pdf (page 12)\",\n",
       "  '구글이 앤스로픽에 투자한 금액은 최대 20억 달러입니다. 이 중 5억 달러를 우선 투자하고, 향후 15억 달러를 추가로 투자할 계획입니다.\\n\\n**Source**\\n- data/SPRI_AI_Brief_2023년12월호_F.pdf (page 13)'],\n",
       " 'contexts': [['SPRi AI Brief |\\n2023-12월호\\n삼성전자, 자체 개발 생성 AI ‘삼성 가우스’ 공개\\nKEY Contents\\nn 삼성전자가 온디바이스에서 작동 가능하며 언어, 코드, 이미지의 3개 모델로 구성된 자체 개발 생성\\nAI 모델 ‘삼성 가우스’를 공개\\nn 삼성전자는 삼성 가우스를 다양한 제품에 단계적으로 탑재할 계획으로, 온디바이스 작동이 가능한\\n삼성 가우스는 외부로 사용자 정보가 유출될 위험이 없다는 장점을 보유\\n£언어, 코드, 이미지의 3개 모델로 구성된 삼성 가우스, 온디바이스 작동 지원',\n",
       "   '£언어, 코드, 이미지의 3개 모델로 구성된 삼성 가우스, 온디바이스 작동 지원\\nn 삼성전자가 2023년 11월 8일 열린 ‘삼성 AI 포럼 2023’ 행사에서 자체 개발한 생성 AI 모델\\n‘삼성 가우스’를 최초 공개\\n∙ 정규분포 이론을 정립한 천재 수학자 가우스(Gauss)의 이름을 본뜬 삼성 가우스는 다양한 상황에\\n최적화된 크기의 모델 선택이 가능\\n∙ 삼성 가우스는 라이선스나 개인정보를 침해하지 않는 안전한 데이터를 통해 학습되었으며,\\n온디바이스에서 작동하도록 설계되어 외부로 사용자의 정보가 유출되지 않는 장점을 보유',\n",
       "   '▹ 삼성전자, 자체 개발 생성 AI ‘삼성 가우스’ 공개 ···························································10\\n▹ 구글, 앤스로픽에 20억 달러 투자로 생성 AI 협력 강화 ················································11\\n▹ IDC, 2027년 AI 소프트웨어 매출 2,500억 달러 돌파 전망···········································12',\n",
       "   '온디바이스에서 작동하도록 설계되어 외부로 사용자의 정보가 유출되지 않는 장점을 보유\\n∙ 삼성전자는 삼성 가우스를 활용한 온디바이스 AI 기술도 소개했으며, 생성 AI 모델을 다양한 제품에\\n단계적으로 탑재할 계획\\nn 삼성 가우스는 △텍스트를 생성하는 언어모델 △코드를 생성하는 코드 모델 △이미지를 생성하는\\n이미지 모델의 3개 모델로 구성\\n∙ 언어 모델은 클라우드와 온디바이스 대상 다양한 모델로 구성되며, 메일 작성, 문서 요약, 번역 업무의\\n처리를 지원',\n",
       "   '어시스턴트를 적용한 구글 픽셀(Pixel)과 경쟁할 것으로 예상\\n☞ 출처 : 삼성전자, ‘삼성 AI 포럼’서 자체 개발 생성형 AI ‘삼성 가우스’ 공개, 2023.11.08.\\n삼성전자, ‘삼성 개발자 콘퍼런스 코리아 2023’ 개최, 2023.11.14.\\nTechRepublic, Samsung Gauss: Samsung Research Reveals Generative AI, 2023.11.08.\\n10',\n",
       "   '처리를 지원\\n∙ 코드 모델 기반의 AI 코딩 어시스턴트 ‘코드아이(code.i)’는 대화형 인터페이스로 서비스를 제공하며\\n사내 소프트웨어 개발에 최적화\\n∙ 이미지 모델은 창의적인 이미지를 생성하고 기존 이미지를 원하는 대로 바꿀 수 있도록 지원하며\\n저해상도 이미지의 고해상도 전환도 지원\\nn IT 전문지 테크리퍼블릭(TechRepublic)은 온디바이스 AI가 주요 기술 트렌드로 부상했다며,\\n2024년부터 가우스를 탑재한 삼성 스마트폰이 메타의 라마(Llama)2를 탑재한 퀄컴 기기 및 구글'],\n",
       "  ['기업 허깅 페이스(Hugging Face)에도 투자\\n∙ 구글은 챗GPT의 기반 기술과 직접 경쟁할 수 있는 차세대 LLM ‘제미니(Gemini)’를 포함한 자체 AI\\n시스템 개발에도 수십억 달러를 투자했으며, 2024년 제미니를 출시할 계획\\n☞ 출처 : The Wall Street Journal, Google Commits $2 Billion in Funding to AI Startup Anthropic, 2023.10.27.',\n",
       "   '투자하기로 하면서 오픈AI의 지분 49%를 확보했으며, 오픈AI는 마이크로소프트의 애저(Azure)\\n클라우드 플랫폼을 사용해 AI 모델을 훈련\\n£구글, 클라우드 경쟁력 강화를 위해 생성 AI 투자 확대\\nn 구글은 수익률이 높은 클라우드 컴퓨팅 시장에서 아마존과 마이크로소프트를 따라잡고자 생성 AI를\\n통한 기업 고객의 클라우드 지출 확대를 위해 AI 투자를 지속\\n∙ 구글은 앤스로픽 외에도 AI 동영상 제작 도구를 개발하는 런웨이(Runway)와 오픈소스 소프트웨어\\n기업 허깅 페이스(Hugging Face)에도 투자',\n",
       "   'Bloomberg, AI Startup Anthropic to Use Google Chips in Expanded Partnership, 2023.11.09.',\n",
       "   '1. 정책/법제 2. 기업/산업 3. 기술/연구 4. 인력/교육\\n구글, 앤스로픽에 20억 달러 투자로 생성 AI 협력 강화\\nKEY Contents\\nn 구글이 앤스로픽에 최대 20억 달러 투자에 합의하고 5억 달러를 우선 투자했으며, 앤스로픽은\\n구글과 클라우드 서비스 사용 계약도 체결\\nn 3대 클라우드 사업자인 구글, 마이크로소프트, 아마존은 차세대 AI 모델의 대표 기업인\\n앤스로픽 및 오픈AI와 협력을 확대하는 추세\\n£구글, 앤스로픽에 최대 20억 달러 투자 합의 및 클라우드 서비스 제공',\n",
       "   '£구글, 앤스로픽에 최대 20억 달러 투자 합의 및 클라우드 서비스 제공\\nn 구글이 2023년 10월 27일 앤스로픽에 최대 20억 달러를 투자하기로 합의했으며, 이 중 5억\\n달러를 우선 투자하고 향후 15억 달러를 추가로 투자할 방침\\n∙ 구글은 2023년 2월 앤스로픽에 이미 5억 5,000만 달러를 투자한 바 있으며, 아마존도 지난 9월\\n앤스로픽에 최대 40억 달러의 투자 계획을 공개\\n∙ 한편, 2023년 11월 8일 블룸버그 보도에 따르면 앤스로픽은 구글의 클라우드 서비스 사용을 위해\\n4년간 30억 달러 규모의 계약을 체결',\n",
       "   '▹ 삼성전자, 자체 개발 생성 AI ‘삼성 가우스’ 공개 ···························································10\\n▹ 구글, 앤스로픽에 20억 달러 투자로 생성 AI 협력 강화 ················································11\\n▹ IDC, 2027년 AI 소프트웨어 매출 2,500억 달러 돌파 전망···········································12']]}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 샘플을 출력\n",
    "evaluator.get_samples()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "단일 평가를 진행합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "1444ef83c42a485b9df78300d0072025",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Evaluating:   0%|          | 0/2 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>user_input</th>\n",
       "      <th>retrieved_contexts</th>\n",
       "      <th>response</th>\n",
       "      <th>answer_relevancy</th>\n",
       "      <th>faithfulness</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>구글이 anthropic 에 투자한 금액은 얼마인가요?</td>\n",
       "      <td>[기업 허깅 페이스(Hugging Face)에도 투자\\n∙ 구글은 챗GPT의 기반 ...</td>\n",
       "      <td>구글이 앤스로픽에 투자한 금액은 최대 20억 달러입니다. 이 중 5억 달러를 우선 ...</td>\n",
       "      <td>0.892348</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                       user_input  \\\n",
       "0  구글이 anthropic 에 투자한 금액은 얼마인가요?   \n",
       "\n",
       "                                  retrieved_contexts  \\\n",
       "0  [기업 허깅 페이스(Hugging Face)에도 투자\\n∙ 구글은 챗GPT의 기반 ...   \n",
       "\n",
       "                                            response  answer_relevancy  \\\n",
       "0  구글이 앤스로픽에 투자한 금액은 최대 20억 달러입니다. 이 중 5억 달러를 우선 ...          0.892348   \n",
       "\n",
       "   faithfulness  \n",
       "0           1.0  "
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 평가 결과 출력\n",
    "evaluate_last = evaluator.evaluate_last()\n",
    "evaluate_last"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "✅ 평가 결과\n",
      "- 관련성 점수: 0.892\n",
      "- 신뢰도 점수: 1.000\n"
     ]
    }
   ],
   "source": [
    "print(\n",
    "    f'✅ 평가 결과\\n- 관련성 점수: {evaluate_last.iloc[0][\"answer_relevancy\"]:.3f}\\n- 신뢰도 점수: {evaluate_last.iloc[0][\"faithfulness\"]:.3f}'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "종합 평가를 진행합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "1896ec8f46094a0da021b0e2d3ec8d33",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Evaluating:   0%|          | 0/4 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>user_input</th>\n",
       "      <th>retrieved_contexts</th>\n",
       "      <th>response</th>\n",
       "      <th>answer_relevancy</th>\n",
       "      <th>faithfulness</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>삼성전자가 만든 생성형 AI 모델은 무엇인가요?</td>\n",
       "      <td>[SPRi AI Brief |\\n2023-12월호\\n삼성전자, 자체 개발 생성 AI...</td>\n",
       "      <td>삼성전자가 만든 생성형 AI 모델은 '삼성 가우스'입니다.\\n\\n**Source**...</td>\n",
       "      <td>0.906562</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>구글이 anthropic 에 투자한 금액은 얼마인가요?</td>\n",
       "      <td>[기업 허깅 페이스(Hugging Face)에도 투자\\n∙ 구글은 챗GPT의 기반 ...</td>\n",
       "      <td>구글이 앤스로픽에 투자한 금액은 최대 20억 달러입니다. 이 중 5억 달러를 우선 ...</td>\n",
       "      <td>0.892348</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                       user_input  \\\n",
       "0      삼성전자가 만든 생성형 AI 모델은 무엇인가요?   \n",
       "1  구글이 anthropic 에 투자한 금액은 얼마인가요?   \n",
       "\n",
       "                                  retrieved_contexts  \\\n",
       "0  [SPRi AI Brief |\\n2023-12월호\\n삼성전자, 자체 개발 생성 AI...   \n",
       "1  [기업 허깅 페이스(Hugging Face)에도 투자\\n∙ 구글은 챗GPT의 기반 ...   \n",
       "\n",
       "                                            response  answer_relevancy  \\\n",
       "0  삼성전자가 만든 생성형 AI 모델은 '삼성 가우스'입니다.\\n\\n**Source**...          0.906562   \n",
       "1  구글이 앤스로픽에 투자한 금액은 최대 20억 달러입니다. 이 중 5억 달러를 우선 ...          0.892348   \n",
       "\n",
       "   faithfulness  \n",
       "0           1.0  \n",
       "1           1.0  "
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 평가 결과 출력\n",
    "evaluator.evaluate_all()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain-kr-lwwSZlnu-py3.11",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
